package com.xdxc.utils;

import com.xdxc.emuns.QuestionType;
import com.xdxc.mapper.UserAnswerMapper;
import com.xdxc.model.dto.AnswerDto;
import com.xdxc.model.dto.UserAnswerDto;
import com.xdxc.model.entity.UserAnswer;
import com.xdxc.service.UserAnswerService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

@Slf4j
@Component
public class MergeWindowManager {
    // 核心数据结构：线程安全的合并窗口
    private final ConcurrentHashMap<String, WindowBucket> windowMap = new ConcurrentHashMap<>();

    // 合并窗口配置
    private static final long WINDOW_DURATION_MS = 5000; // 5秒合并窗口
    private static final int MAX_BATCH_SIZE = 2;       // 最大批量提交条数
    private static final int FLUSH_INTERVAL_MS = 1000;   // 刷写检查间隔

    // 时间轮调度器（单线程）
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    // 合并策略工厂（根据题目类型选择策略）

//    private final AnswerMergeStrategyFactory strategyFactory;
    // 性能监控指标
    private final AtomicLong totalMerged = new AtomicLong(0);
    private final AtomicLong totalFlushed = new AtomicLong(0);
    @Autowired
    private UserAnswerMapper userAnswerMapper;

    @Autowired
    private UserAnswerService userAnswerService;

//    public MergeWindowManager(AnswerMergeStrategyFactory strategyFactory) {
//        this.strategyFactory = strategyFactory;
//    }

    @PostConstruct
    public void init() {
        // 启动定时刷写任务
        scheduler.scheduleAtFixedRate(this::flushExpiredBuckets,
                FLUSH_INTERVAL_MS, FLUSH_INTERVAL_MS, TimeUnit.MILLISECONDS);
    }

    @PreDestroy
    public void shutdown() {
        // 优雅关闭时强制刷出剩余数据
        flushAllBuckets();
        scheduler.shutdown();
    }

    /**
     * 累积消息到合并窗口
     */
    public void accumulate(UserAnswerDto dto) {
        String compositeKey = buildCompositeKey(dto);
        windowMap.compute(compositeKey, (key, bucket) -> {
            if (bucket == null) {
                return new WindowBucket(dto);
            } else {
                return mergeBucket(bucket, dto);
            }
        });
        checkBatchSize();
    }

    /**
     * 构建复合键：examId:userId:questionId
     */
    private String buildCompositeKey(UserAnswerDto dto) {
        // 确保使用题目ID + 用户ID + 试卷ID作为唯一键
        return String.format("%d_%d_%d",
                dto.getQuId(),      // 题目ID
                dto.getUserId(),     // 用户ID
                dto.getPaperId()     // 试卷ID
        );
    }
    /**
     * 合并窗口桶
     */
    private WindowBucket mergeBucket(WindowBucket existing, UserAnswerDto newDto) {
//        AnswerMergeStrategy strategy = strategyFactory.getStrategy(newDto.getQuestionType());
//        String mergedData = strategy.merge(existing.getLatestDTO().getAnswerData(), newDto.getAnswerData());

        UserAnswerDto mergedDto = new UserAnswerDto();
        BeanUtils.copyProperties(newDto, mergedDto);
//        mergedDto.set(mergedData);
        mergedDto.setVersion(existing.getLatestDTO().getVersion() + 1);

        existing.setLatestDTO(mergedDto);
        existing.setUpdateCount(existing.getUpdateCount() + 1);
        existing.setLastUpdateTime(System.currentTimeMillis());
        return existing;
    }

    /**
     * 定时刷写过期桶
     */
    private void flushExpiredBuckets() {
        long currentTime = System.currentTimeMillis();
        List<WindowBucket> expiredBuckets = windowMap.values().stream()
                .filter(bucket -> (currentTime - bucket.getLastUpdateTime()) > WINDOW_DURATION_MS)
                .collect(Collectors.toList());

        List<UserAnswer> userAnswerList =new ArrayList<>();
        expiredBuckets.forEach(bucket -> {
            windowMap.remove(bucket.getCompositeKey());
            UserAnswer userAnswer = new UserAnswer();
            userAnswer.setUserId(bucket.getLatestDTO().getUserId());
            userAnswer.setId(new RandomIdGenerator(1,5).generateId());
            userAnswer.setAnswerIds(bucket.getLatestDTO().getAnswerIds());
            userAnswer.setAnswerText(bucket.getLatestDTO().getAnswerText());
            userAnswer.setPaperId(bucket.getLatestDTO().getPaperId());
            userAnswer.setAnswerType(QuestionType.getName(bucket.getLatestDTO().getAnswerType()));
            userAnswer.setQuId(bucket.getLatestDTO().getQuId());
            userAnswer.setVersion(bucket.getLatestDTO().getVersion());
            userAnswer.setExamId(bucket.getLatestDTO().getExamId());
            userAnswer.setCheckId(bucket.getLatestDTO().getCheckId());
            userAnswerList.add(userAnswer);
            totalFlushed.incrementAndGet();
        });
        submitToBatchService(userAnswerList);

        log.debug("定时刷出 {} 条数据", expiredBuckets.size());
    }

    /**
     * 批量大小检查
     */
    private void checkBatchSize() {
        if (windowMap.size() >= MAX_BATCH_SIZE) {
            flushAllBuckets();
        }
    }

    /**
     * 强制刷出所有数据
     */
    private void flushAllBuckets() {
        List<WindowBucket> allBuckets = new ArrayList<>(windowMap.values());
        windowMap.clear();

        List<UserAnswer> userAnswerList = new ArrayList<>();
        allBuckets.forEach(bucket -> {
            UserAnswer userAnswer = new UserAnswer();
            userAnswer.setUserId(bucket.getLatestDTO().getUserId());
            userAnswer.setId(new RandomIdGenerator(1,5).generateId());
            userAnswer.setAnswerIds(bucket.getLatestDTO().getAnswerIds());
            userAnswer.setAnswerText(bucket.getLatestDTO().getAnswerText());
            userAnswer.setPaperId(bucket.getLatestDTO().getPaperId());
            userAnswer.setAnswerType(QuestionType.getName(bucket.getLatestDTO().getAnswerType()));
            userAnswer.setQuId(bucket.getLatestDTO().getQuId());
            userAnswer.setVersion(bucket.getLatestDTO().getVersion());
            userAnswer.setExamId(bucket.getLatestDTO().getExamId());
            userAnswer.setCheckId(bucket.getLatestDTO().getCheckId());
            userAnswerList.add(userAnswer);
            totalFlushed.addAndGet(bucket.getUpdateCount());
        });
        submitToBatchService(userAnswerList);

        log.info("批量刷出 {} 条数据", allBuckets.size());
    }

    /**
     * 提交到批量处理服务
     */
    private void submitToBatchService(List<UserAnswer> dto) {
        // 实际业务中调用批量存储服务
        userAnswerMapper.batchUpsert(dto);

    }

    /**
     * 窗口桶数据结构
     */
    @Data
    private static class WindowBucket {
        private final String compositeKey;
        private UserAnswerDto latestDTO;
        private long createTime;
        private long lastUpdateTime;
        private int updateCount = 1;

        public WindowBucket(UserAnswerDto dto) {

            this.compositeKey = String.format("%d:%d:%d",
                    dto.getPaperId(),  dto.getUserId(), dto.getCheckId());
            this.latestDTO = dto;
            this.createTime = System.currentTimeMillis();
            this.lastUpdateTime = this.createTime;
        }
    }

    // 监控端点
    public Map<String, Number> getMetrics() {
        return Map.of(
                "windowSize", windowMap.size(),
                "totalMerged", totalMerged.get(),
                "totalFlushed", totalFlushed.get()
        );
    }
}